#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "lini_parse.h"
#include "lini_typedef.h"
#include "lini_builder.h"


struct _LiniParser
{	
    LiniBuilder *builder;
};

static int ini_parser_handle_group(LiniParser *thiz, char *group)
{
	return_val_if_fail(thiz && thiz->builder && group,  -1);

	lini_builder_on_group(thiz->builder, group);

	return 0;
}

static int ini_parser_handle_key_value(LiniParser *thiz, char *key, char *value)
{
	return_val_if_fail(thiz && thiz->builder && key, -1);
	
	lini_builder_on_key_value(thiz->builder, key, value);

	return 0;
}

static int ini_parser_handle_comment(LiniParser *thiz, char *comment)
{
	return_val_if_fail(thiz && thiz->builder && comment, -1);

	lini_builder_on_comment(thiz->builder, comment);

	return 0;
}

LiniParser *lini_parser_create()
{
	LiniParser *thiz = NULL;

	thiz = (LiniParser *)malloc(sizeof(LiniParser));
	
	return thiz;
}

void lini_parser_set_builder(LiniParser *thiz, LiniBuilder *builder)
{	
	return_if_fail(thiz != NULL);

	thiz->builder = builder;
    builder->ref_count++;

	return;
}

#define MAX_BUF_SIZE 128


static void rtrim(char *s)
{
    int l=0,p=0;
    l = strlen(s);
    if( l == 0 ) return;
    p = l -1;
    while( s[p] == ' ' || s[p] == '\t' ) {
        s[p--] = '\0';
        if( p < 0 ) break;
    }
    return;
}

void lini_parser_parse(LiniParser *thiz, char *ini, char *comment_char)
{
	return_if_fail(thiz != NULL && ini != NULL );

	char *group_start = NULL;
	char *comment_start = NULL;
	char *key_start = NULL;
	char *value_start = NULL;
	char *p = NULL;
	unsigned char utf8_bytes[3] = {0xEF, 0xBB, 0xBF};
	
	if(comment_char == NULL){
		*comment_char = '=';
	}
	
	enum _IniState
	{
		STATE_NONE,
		STATE_GROUP,
		STATE_KEY,
		STATE_VALUE,
		STATE_COMMENT,
	}state = STATE_NONE;

	if(memcmp( ini, utf8_bytes, 3) == 0){
		ini+=3;
	}

	for(p =  ini; *p != '\0'; p++){
		switch(state){
			case STATE_NONE:{	
				if(*p == '['){
					state = STATE_GROUP;
					group_start = p + 1;
				}
				else if(*p == *comment_char){
					state = STATE_COMMENT;
					comment_start = p + 1;
				}
				else if(!isspace(*p)){

					state = STATE_KEY;
					key_start = p;
				}
				break;
			}
			case STATE_GROUP:{
				if(*p == ']'){
					state = STATE_NONE;
					*p = '\0';

					ini_parser_handle_group(thiz, group_start);	
				}
				break;
			}
			case STATE_KEY:{
				if(*p == '='){
					state = STATE_VALUE;
					value_start = p + 1;
					*p = '\0';
				}
				break;
			}
			case STATE_VALUE:{
				if(*p == '\r' || *p == '\n'){
					*p = '\0';
					state = STATE_NONE;
					rtrim(value_start);
					ini_parser_handle_key_value(thiz, key_start, value_start);
					key_start = NULL;
					value_start = NULL;
				}
				break;
			}
			
			case STATE_COMMENT:{
				if(*p == '\n' || *p == '\r'){
					*p = '\0';
					state = STATE_NONE;
					ini_parser_handle_comment(thiz, comment_start);
				}
				break;
			}
			default:
				break;
		}
	}

	return;
}

void lini_parser_destory(LiniParser *thiz)
{
	if(thiz){
		free(thiz);
	}

	return;
}


